#include "ulineedit.h"
#include "utooltip.h"
#include "ulabel.h"

#include <QPainter>
#include <QStyleOptionFrame>
#include <QDebug>
#include <QHBoxLayout>
#include <QAction>
#include <QTimer>
#include <QStylePainter>
#include <QPainterPath>
#include <QMouseEvent>

ULineEditButton::ULineEditButton(QWidget* parent)
    : QPushButton(parent)
{
    setFlat(true);
    setCheckable(true);
}

void ULineEditButton::setRadius(int radius)
{
    if (m_radius != radius)
    {
        m_radius = radius;
        update();
    }
}

void ULineEditButton::setBackGroundColor(const QColor &color)
{
    if (m_color != color)
    {
        m_color = color;
        update();
    }
}

void ULineEditButton::paintEvent(QPaintEvent* event)
{
    Q_UNUSED(event)

    QStyleOptionButton btn;
    initStyleOption(&btn);

    QStylePainter stylePainter(this);
    stylePainter.setRenderHint(QPainter::Antialiasing);

    QColor brush = m_color.isValid() ? m_color : palette().color(QPalette::Base);
    stylePainter.setBrush(brush);
    stylePainter.setPen(Qt::NoPen);
    if (btn.state & QStyle::State_Enabled)
    {
        if (btn.state & QStyle::State_MouseOver)
            stylePainter.setBrush(brush.darker(120));
    }

    if (isDown())
        stylePainter.setBrush(brush.darker(150));

    QPainterPath painterPath;
    painterPath.addRoundedRect(rect(), m_radius, m_radius);
    stylePainter.drawPath(painterPath);

    style()->drawControl(QStyle::CE_PushButtonLabel, &btn, &stylePainter, this);
}

class ULineEditPrivate
{
public:
    ULineEditPrivate(ULineEdit* q) : q_ptr(q) {}
    ~ULineEditPrivate() {}

    void init();

    void initLineEdit();

    void initToolTip();

    void initConnection();

    QRect closeButtonRect();

    void setButtonVisible(bool isVisible);

    void drawCloseButton(QPainter *painter);

private:
    Q_DECLARE_PUBLIC(ULineEdit)
    Q_DISABLE_COPY(ULineEditPrivate)
    ULineEdit* const q_ptr = nullptr;

    int m_radius = 8;
    bool m_isAlert = false;
    ULabel* label = nullptr;
    UToolTip* toolTip = nullptr;
    bool m_openCloseButton = true;
    bool buttonVisible = false;
    double m_borderWidth = 1.5;
    QSize m_buttonSize;
    bool m_isHover = false;
    bool m_isDown = false;
    bool m_borderHoverEnabled = true;
};

void ULineEditPrivate::init()
{
    initLineEdit();
    initToolTip();
    initConnection();
}

void ULineEditPrivate::initLineEdit()
{
    Q_Q(ULineEdit);
    q->setFrame(false);
    q->setFocusPolicy(Qt::FocusPolicy::StrongFocus);
    q->setMouseTracking(true);

    QHBoxLayout* hLayout = new QHBoxLayout;
    hLayout->setContentsMargins(6, 6, 6, 6);
    q->setLayout(hLayout);

    label = new ULabel(q);
    label->setVisible(false);
    label->setObjectName("ULineEdit_LeftAction");
    hLayout->addWidget(label);
    hLayout->setAlignment(label, Qt::AlignLeft | Qt::AlignVCenter);
}

void ULineEditPrivate::initToolTip()
{
    Q_Q(ULineEdit);
    toolTip = new UToolTip(q);
    toolTip->setVisible(false);
    toolTip->setWindowFlags(q->windowFlags() | Qt::ToolTip);
    toolTip->setTextColor(QColor(241, 57, 50));
}

void ULineEditPrivate::initConnection()
{
    Q_Q(ULineEdit);
    q->connect(q, &ULineEdit::textChanged, q, [ = ](const QString &)
    {
        if (!m_openCloseButton)
        {
            return;
        }
        setButtonVisible(!q->text().isEmpty());
    });

    q->connect(q, &ULineEdit::closeButtonClicked, q, [ = ]()
    {
        q->clear();
        setButtonVisible(false);
    });
}

QRect ULineEditPrivate::closeButtonRect()
{
    Q_Q(ULineEdit);
    int rightPadding = 6;
    QPoint point = q->rect().topRight();
    point = QPoint(point.x() - rightPadding - m_buttonSize.width(), (q->height() - m_buttonSize.height()) / 2);
    return QRect(point, m_buttonSize);
}

void ULineEditPrivate::setButtonVisible(bool isVisible)
{
    Q_Q(ULineEdit);
    if (buttonVisible != isVisible)
    {
        buttonVisible = isVisible;
        q->update();
    }
}

void ULineEditPrivate::drawCloseButton(QPainter* painter)
{
    Q_Q(ULineEdit);
    if (m_openCloseButton && buttonVisible)
    {
        painter->save();
        QColor brush = q->palette().color(QPalette::Base);
        painter->setBrush(brush);
        painter->setPen(Qt::NoPen);
        if (q->isEnabled() && m_isHover)
        {
            painter->setBrush(brush.darker(120));
        }

        if (m_isDown)
            painter->setBrush(brush.darker(150));

        QPainterPath painterPath;
        QRect buttonRect = closeButtonRect();
        painterPath.addRoundedRect(buttonRect, buttonRect.width() / 2, buttonRect.width() / 2);
        painter->drawPath(painterPath);
        QIcon icon = QIcon::fromTheme("close");
        int wid = buttonRect.width() / 4 + 1;
        QRect closeRect = QRect(buttonRect.x() + wid, buttonRect.y() + wid, buttonRect.width() / 2, buttonRect.width() / 2);
        icon.paint(painter, closeRect, Qt::AlignCenter);
        painter->restore();
    }
}

ULineEdit::ULineEdit(QWidget* parent) : QLineEdit(parent)
    , d_ptr(new ULineEditPrivate(this))
{
    Q_D(ULineEdit);
    d->init();
}

ULineEdit::ULineEdit(const QString &text, QWidget* parent) : QLineEdit(text, parent)
    , d_ptr(new ULineEditPrivate(this))
{
    Q_D(ULineEdit);
    d->init();
}

ULineEdit::~ULineEdit()
{
}

void ULineEdit::setHoverEnabled(bool enabled)
{
    Q_D(ULineEdit);
    if (d->m_borderHoverEnabled != enabled)
    {
        d->m_borderHoverEnabled = enabled;
        update();
    }
}

void ULineEdit::setBorderWidth(double width)
{
    Q_D(ULineEdit);
    if (d->m_borderWidth != width)
    {
        d->m_borderWidth = width;
        update();
    }
}

void ULineEdit::setRadius(int radius)
{
    Q_D(ULineEdit);
    if (d->m_radius != radius)
    {
        d->m_radius = radius;
        update();
    }
}

bool ULineEdit::openCloseButton()
{
    Q_D(ULineEdit);
    return d->m_openCloseButton;
}

void ULineEdit::setOpenCloseButton(bool isOpen)
{
    Q_D(ULineEdit);
    d->m_openCloseButton = isOpen;
}

void ULineEdit::addLeftAction(const QIcon &icon, const QSize &iconSize)
{
    Q_D(ULineEdit);
    if (!icon.isNull())
    {
        d->label->setIcon(icon);
        d->label->setFixedSize(iconSize);
        d->label->setVisible(true);
    }
}

void ULineEdit::addLeftAction(const QString &text)
{
    Q_D(ULineEdit);
    if (!text.trimmed().isEmpty())
    {
        d->label->setText(text);
        d->label->setVisible(true);
    }
}

void ULineEdit::clearAction()
{
    Q_D(ULineEdit);
    d->label->setVisible(false);
}

bool ULineEdit::isAlert()
{
    Q_D(ULineEdit);
    return d->m_isAlert;
}

void ULineEdit::showAlertMessage(const QString &text, int duration)
{
    Q_D(ULineEdit);
    if ((d->toolTip && d->toolTip->isVisible()) || text.trimmed().isEmpty())
        return;

    if (d->toolTip)
    {
        d->toolTip->move(mapToGlobal(rect().bottomLeft() + QPoint(0, 2)));
        d->toolTip->setText(text);
        d->toolTip->setVisible(true);
    }
    d->m_isAlert = true;
    update();

    QTimer::singleShot(duration, this, &ULineEdit::hideAlertMessage);
}

void ULineEdit::hideAlertMessage()
{
    Q_D(ULineEdit);
    d->m_isAlert = false;
    if (d->toolTip)
        d->toolTip->setVisible(false);

    update();
}

void ULineEdit::paintEvent(QPaintEvent* event)
{
    Q_D(ULineEdit);
    QPainter painter(this);
    painter.setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform);
    qreal opacity = isEnabled() ? 1.0 : 0.3;
    painter.setOpacity(opacity);

    QPainterPath rectPath;
    QRect rect = this->rect().adjusted(d->m_borderWidth, d->m_borderWidth, -d->m_borderWidth, -d->m_borderWidth);
    rectPath.addRoundedRect(rect, d->m_radius, d->m_radius);

    QStyleOption opt;
    opt.init(this);
    QPen pen = QPen(Qt::transparent, d->m_borderWidth);

    if (opt.state & QStyle::State_Enabled)
    {
        if (opt.state & QStyle::State_Selected)
        {
            pen = QPen(palette().color(QPalette::Highlight), d->m_borderWidth);
        }
        else if (opt.state & QStyle::State_MouseOver && d->m_borderHoverEnabled)
        {
            pen = QPen(palette().color(QPalette::Text), d->m_borderWidth);
        }
    }

    QColor backGround = d->m_isAlert ? QColor(241, 57, 50, qRound(0.15 * 255)) : palette().color(QPalette::Button);
    painter.setBrush(backGround);
    painter.setPen(pen);
    painter.drawPath(rectPath);

    QLineEdit::paintEvent(event);

    d->drawCloseButton(&painter);

    if (hasFocus() && d->m_borderWidth > 0)
    {
        painter.save();
        painter.setBrush(Qt::NoBrush);
        painter.setPen(QPen(palette().color(QPalette::Highlight), d->m_borderWidth));
        painter.drawPath(rectPath);
        painter.restore();
    }
}

void ULineEdit::resizeEvent(QResizeEvent* event)
{
    Q_D(ULineEdit);
    auto margins = textMargins();
    int size = height() - layout()->contentsMargins().top() * 2;
    d->m_buttonSize = QSize(size, size);

    int leftPadding = 6;
    int rightPadding = 6;
    if (!d->label->isHidden())
        leftPadding = leftPadding + d->label->width() + 5;

    if (d->m_openCloseButton)
        rightPadding = rightPadding + d->m_buttonSize.width() + 5;

    setTextMargins(leftPadding, margins.top(), rightPadding, margins.bottom());

    QLineEdit::resizeEvent(event);
}

void ULineEdit::mousePressEvent(QMouseEvent* event)
{
    Q_D(ULineEdit);
    if (!d->m_openCloseButton)
    {
        QLineEdit::mousePressEvent(event);
        return;
    }
    d->m_isDown = d->closeButtonRect().contains(event->pos());
    QLineEdit::mousePressEvent(event);
    update();
}

void ULineEdit::mouseMoveEvent(QMouseEvent* event)
{
    Q_D(ULineEdit);
    if (!d->m_openCloseButton)
    {
        QLineEdit::mouseMoveEvent(event);
        return;
    }
    d->m_isHover = d->closeButtonRect().contains(event->pos());
    QLineEdit::mouseMoveEvent(event);
    update();
}

void ULineEdit::mouseReleaseEvent(QMouseEvent* event)
{
    Q_D(ULineEdit);
    if (d->m_isDown)
    {
        d->m_isDown = false;
        emit closeButtonClicked();
        update();
    }
    else
    {
        QLineEdit::mouseReleaseEvent(event);
    }
}
